﻿using SuperSocket;
using System.Threading.Tasks;
using System.Linq;
using System.Buffers;
using Xc.DataAccess.Core.Extensions;
using Xc.DataAccess.Protocoals.WaterMeters.Models;
using Xc.DataAccess.Protocoals.WaterMeters.Models.Responds;
using System.Collections.ObjectModel;
using SuperSocket.Channel;
using System.Collections.Generic;
using System;
using Microsoft.Extensions.Logging;
using Xc.DataAccess.Core;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Caching.Memory;
using System.Collections.Concurrent;
namespace Xc.DataAccess.Protocoals.WaterMeters.Services
{
    public abstract class MeterHandleService : IMeterHandleService
    {
        private readonly ConcurrentDictionary<uint, Type> keyValuePairs = new ConcurrentDictionary<uint, Type>();
        private readonly ILogger<IMeterHandleService> logger;
        public MeterHandleService(ILogger<IMeterHandleService> logger)
        {
            jsonSerializerOptions = new JsonSerializerOptions() { WriteIndented = true };
            jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());

            var types = typeof(IMeterHandleService).Assembly.GetTypes().Where(p => p.IsAssignableTo(typeof(MeterFrameRxDataSegmentBase)) && p.IsAbstract == false && p.IsPublic).ToArray();
            foreach (var item in types)
            {
                var cmd = (MeterFrameRxDataSegmentBase)Activator.CreateInstance(item, new object[] { null });
                Init(cmd.ValidateCmd.ToArray(), cmd.ValidateDataSegment, item, keyValuePairs);
            }
            this.logger = logger;
        }
        private async void Init(FrameCommand[] frameCommand, DataSegment dataSegment, Type type, ConcurrentDictionary<uint, Type> keyValuePairs)
        {
            foreach (var cmd in frameCommand)
            {
                uint key = (uint)(((byte)cmd) * 0x10000 + (ushort)dataSegment);
                if (!keyValuePairs.ContainsKey(key))
                {
                    while (keyValuePairs.TryAdd(key, type)==false)
                    {
                       await Task.Delay(10);
                    }
                }
            }
        }
        /// <summary>
        /// 请求之前触发
        /// </summary>
        /// <typeparam name="TData"></typeparam>
        /// <param name="data"></param>
        /// <returns></returns>
        public  abstract Result BeforeRequest<TData>(TData data) where TData : MeterFrameTxBase;
        /// <summary>
        /// 发起请求
        /// </summary>
        /// <typeparam name="TData"></typeparam>
        /// <param name="data"></param>
        /// <returns></returns>
        public virtual async  ValueTask<ResultData<TData,byte[]>> AfterRequest<TData>(TData data) where TData : MeterFrameTxBase
        {

            try
            {
                var d = BeforeRequest(data);
                if (d.IsError)
                {
                    return ResultData<TData, byte[]>.Reject(d.Message, data, null);
                }
                var c = Online.Where(p => p.Value.DeviceId == data.DevcieId).FirstOrDefault();
                if (c.Value == null)
                {
                    return ResultData<TData, byte[]>.Reject("设备未在线或不存在", data, null);
                }
                var p = c.Value;
                if (p == null)
                {
                    return ResultData<TData, byte[]>.Reject("设备未在线或不存在",data,null);
                }
                if (p.DeviceId == null)
                {
                    return ResultData<TData, byte[]>.Reject("设备编号不存在，请先上报", data,null);

                }
                if (p.MeterType == null)
                {
                    return ResultData<TData, byte[]>.Reject("表具类型不存在，请先上报", data,null);
                }
                if (p.CompanyId == null)
                {
                    return ResultData<TData, byte[]>.Reject("厂商ID不存在，请先上报", data,null);
                }
                data.DevcieId = p.DeviceId;
                data.MeterType = p.MeterType.Value;
                data.CompanyId = p.CompanyId.Value;
                var array = data.FullFrame.ToArray();
                await p.SendAsync(array);
                logger.LogInformation($"DeviceServer To Device [{data.DevcieId}] :{array.ToHex()}");
                return ResultData<TData, byte[]>.Accept("发送成功",data,array);
            }
            catch (Exception err)
            {
                return ResultData<TData, byte[]>.Reject(err.Message,data,null);
            }

        }
        /// <summary>
        /// 设备连接时调用
        /// </summary>
        /// <param name="devcieInfoAppSession"></param>
        protected virtual void OnDeviceConnect(IDevcieInfoAppSession devcieInfoAppSession)
        {

        }
        /// <summary>
        /// 在线列表
        /// </summary>
        public ConcurrentDictionary<string,IDevcieInfoAppSession> Online { get; private set; } = new ConcurrentDictionary<string, IDevcieInfoAppSession>();
        /// <summary>
        /// 连接时调用
        /// </summary>
        /// <param name="appSession"></param>
        /// <returns></returns>
        public ValueTask OnConnect(IDevcieInfoAppSession appSession)
        {
            if (Online.ContainsKey(appSession.SessionID))
            {
                Online.Remove(appSession.SessionID, out _);
            }
            while (!Online.TryAdd(appSession.SessionID, appSession)==false)
            {

            }
            OnDeviceConnect(appSession);
            return ValueTask.CompletedTask;
        }
        /// <summary>
        /// 关闭时调用
        /// </summary>
        /// <param name="appSession"></param>
        /// <param name="closeEventArgs"></param>
        /// <returns></returns>
        public ValueTask OnClose(IDevcieInfoAppSession appSession, CloseEventArgs closeEventArgs)
        {
            if (Online.ContainsKey(appSession.SessionID))
            {
                Online.Remove(appSession.SessionID, out _);
            }
            return ValueTask.CompletedTask;

        }
        public ValueTask PackageHandle(IDevcieInfoAppSession appSession, MeterFrameRx meterFrameRx)
        {
            var frame = new EmptyMeterFrameRxDataSegment(meterFrameRx);
            appSession.DeviceId = meterFrameRx.MeterAddress.ToHex();
            appSession.MeterType = frame.MeterType;
            appSession.CompanyId = frame.CompanyId;
            logger.LogInformation($"Device [{frame.MeterAddress.ToArray().ToHex()}] To DevcieServer :{meterFrameRx.FullFrame.ToHex()},{appSession.RemoteEndPoint.ToString()}");

            if (frame.MeterType != MeterType.WaterMeter)
            {
                return ValueTask.CompletedTask;
            }
            var type = keyValuePairs[frame.Index];
            var request = Activator.CreateInstance(type, new object[] { meterFrameRx });
            return NotifyData(appSession,type, request, frame.Cmd, frame.DataSegment, frame.MeterAddress.ToArray().ToHex(), frame.MeterType, frame.CompanyId);
        }
        private readonly JsonSerializerOptions jsonSerializerOptions;
        protected virtual ValueTask NotifyData(IDevcieInfoAppSession devcieInfoAppSession, Type type, object data, FrameCommand frameCommand, DataSegment dataSegment, string devcieId, MeterType meterType, byte companyId)
        {
            logger.LogInformation($"DeviceServer To Plantform:{type.Name}");
            logger.LogInformation(System.Text.Json.JsonSerializer.Serialize(data, jsonSerializerOptions));

            return ValueTask.CompletedTask;
        }
        public ValueTask<bool> ErrorHanle(IDevcieInfoAppSession appSession, PackageHandlingException<MeterFrameRx> packageHandlingException)
        {
            logger.LogError($"{appSession.DeviceId}发生异常：{packageHandlingException.Message},完整错误:{packageHandlingException.ToString()}");
            return  new ValueTask<bool>(true);
        }
    }
}
